package members

import (
	"github.com/gorilla/websocket"
	"encoding/json"
	"errors"
	"time"
	"adai.design/homeserver/log"
	"strings"
	"adai.design/homeserver/db"
)

const (
	idleTimeout = time.Second * 16
	verifyTimeout = time.Second * 5
)

type reception struct {
	conn *websocket.Conn

	m  *db.Membership
	mc *db.MembershipClient

	send 	chan *Message
	read 	chan *Message
	task 	chan *Message
}

func (r *reception) writeMessage(msg *Message) error {
	if r.send != nil {
		r.send <- msg
		return nil
	}
	buf, _ := json.Marshal(msg)
	err := r.conn.WriteMessage(websocket.BinaryMessage, buf)
	return err
}

func (r *reception) readMessage() (*Message, error) {
	t, data, err := r.conn.ReadMessage()
	if err != nil {
		return nil, err
	}

	if t != websocket.BinaryMessage {
		return nil, errors.New("websocket message format err")
	}

	var message Message
	err = json.Unmarshal(data, &message)
	if err != nil {
		return nil, err
	}
	return &message, nil
}

// 登录认证
func (r *reception) verify() error {
	r.conn.SetReadDeadline(time.Now().Add(verifyTimeout))
	msg, err := r.readMessage()
	if err != nil {
		return err
	}

	if msg.Path != msgPathLogin || msg.Method != MsgMethodPost {
		return errors.New("invalid login message type")
	}

	m, c, err := login(msg.Data)
	if err != nil {
		msg = &Message{
			Path: msgPathLogin,
			State: "failed",
		}
		r.writeMessage(msg)
		return err
	}

	r.m = m
	r.mc = c

	ack := &loginAck{
		Id: m.Id,
		Session: c.Session,
		Date: c.Last.Format("2006-01-02 15:04:05"),
	}
	data, _ := json.Marshal(ack)
	msg = &Message{
		Path: msgPathLogin,
		State: "ok",
		Data: data,
	}

	r.writeMessage(msg)
	return nil
}

func (r *reception) readLoop() {
	defer close(r.read)
	for {
		r.conn.SetReadDeadline(time.Now().Add(idleTimeout))
		msg, err := r.readMessage()
		if err != nil {
			log.Error("read: %s", err)
			r.conn.Close()
			return
		}
		r.read <- msg
	}
}

func (r *reception) sendLoop() {
	for msg := range r.send {
		r.conn.SetWriteDeadline(time.Now().Add(idleTimeout))
		buf, _ := json.Marshal(msg)
		err := r.conn.WriteMessage(websocket.BinaryMessage, buf)
		if err != nil {
			r.conn.Close()
			return
		}
	}
}

func (r *reception) logout() {
	msg := &Message{
		Path: msgPathLogin,
		Method: MsgMethodPut,
		State: "logout",
	}
	buf, _ := json.Marshal(msg)
	r.conn.WriteMessage(websocket.BinaryMessage, buf)
	r.conn.Close()
}

func (r *reception) disconnect() {
	r.conn.Close()
}

func (r *reception) stop() {
	close(r.task)
}

func (r *reception) start(rs *receptions) {
	log.Debug("rm: %s arrive", r.conn.RemoteAddr())
	defer log.Debug("rm: %s leave", r.conn.RemoteAddr())
	defer r.conn.Close()

	err := r.verify()
	if err != nil {
		log.Error("rm: %s err: %s", r.conn.RemoteAddr(), err)
		db.Invalid(&db.InvalidInfo{
			Addr: r.conn.RemoteAddr().String(),
			Time: time.Now(),
			Type: db.InvalidWebsocket,
		})
		return
	}

	r.read = make(chan *Message, 5)
	go r.readLoop()

	r.send = make(chan *Message, 5)
	defer close(r.send)
	go r.sendLoop()

	r.task = make(chan *Message, 5)

	r.m.Online(r.mc, r.conn.RemoteAddr().String())
	rs.register <- r
	defer func() {
		rs.unregister <- r
		r.m.Offline(r.mc, r.conn.RemoteAddr().String())
	}()

eventLoop:
	for {
		select {
		case msg, ok := <-r.read:
			if !ok {
				break eventLoop
			}
			//log.Debug("msg: %v", msg)
			if h, ok := memberMessageRouter[msg.Path]; ok {
				if h.handle(r, msg) != nil {
					break eventLoop
				}

			// 发送给家庭处理
			} else if strings.Split(msg.Path, "/")[0] == "home" {
				pkg := &MessagePkg{
					Type: PkgTypeContext,
					Ctx: &Context{
						HomeId: msg.Home,
						MemberId: r.m.Id,
						Session: r.mc.Session,
					},
					Msg: msg,
				}
				rs.recPkg <- pkg
			}

		case task, ok := <-r.task:
			if !ok {
				break eventLoop
			}
			r.writeMessage(task)
		}
	}
}






